﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Text;
using Ganosoft.Container;
using Ganosoft.Container.Impl;
using Ganosoft.Container.Utils;
using Ganosoft.DynamicProxy;
using Ganosoft.Aspect;

namespace GanoESB.Component
{
    public abstract class BaseComponent : MarshalByRefObject, IComponent
    {
        protected IContainer container;
        protected ComponentDescriptor descr;
        protected Bindings bindings;
        protected Dictionary<ServiceDescriptor, ServiceHost> hostList;

        [Bean]
        protected IServiceBus serviceBus;

        [Bean]
        protected ILogService logService;

        protected abstract void DoStart();
        protected abstract void DoStop();

        #region IComponent 成员

        public void Start(ComponentDescriptor descriptor)
        {
            try
            {
                bindings = new Bindings();
                     
                hostList = new Dictionary<ServiceDescriptor, ServiceHost>();

                this.descr = descriptor;

                container = AspectContainer.Instance;
                container.Configure(descriptor.DeploymentFile);
                container.Inject(this);

                if (logService != null)
                    logService.Info(GetType(), descr.ComponentName + " component starting...");

                if (serviceBus == null)
                    serviceBus = GetServiceBus();

                List<ServiceDescriptor> list = new List<ServiceDescriptor>();
                foreach (ServiceDescriptor sd in descr.ServiceDescriptors)
                {
                    sd.ComponentName = descr.ComponentName;
                    if (OpenService(sd))
                        list.Add(sd);
                }

                if (serviceBus != null && list.Count > 0)
                    serviceBus.RegisterService(list.ToArray());

                DoStart();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void Stop()
        {
            DoStop();

            foreach (ServiceDescriptor d in hostList.Keys)
            {
                hostList[d].Close();
            }
        }

        #endregion

        private IServiceBus GetServiceBus()
        {
            IServiceBus bus = null;
            string serviceBusUrl = ConfigurationManager.AppSettings["ServiceBusUrl"];
            if (!string.IsNullOrEmpty(serviceBusUrl))
            {
                System.ServiceModel.Channels.Binding binding = null;
                if (serviceBusUrl.StartsWith("net.pipe"))
                    binding = bindings.PipeBinding;
                else if (serviceBusUrl.StartsWith("net.tcp"))
                    binding = bindings.TcpBinding;
                else if (serviceBusUrl.StartsWith("http"))
                    binding = bindings.HttpBinding;

                if (binding != null)
                    bus = ChannelFactory<IServiceBus>.CreateChannel(binding, new EndpointAddress(serviceBusUrl));
            }

            return bus;
        }

        protected bool OpenService(ServiceDescriptor sd)
        {
            ServiceHost host;

            sd.ServiceUrl = string.Format("net.pipe://localhost/{0}/{1}", sd.ComponentName, sd.ServiceName);

            Type interfaceType = Type.GetType(sd.ServiceInterface);
            Type serviceType = Type.GetType(sd.ServiceClazz);

            object target = Activator.CreateInstance(serviceType);
            container.Inject(target);

            object serviceProxy = new ProxyFactory().CreateProxy(serviceType, new ExecutingInterceptor(target, sd), interfaceType);
            host = new ServiceHost(serviceProxy, new Uri(sd.ServiceUrl));

            host.AddServiceEndpoint(interfaceType, bindings.PipeBinding, "");

            // 重要！创建host时用的是Instance, 只能采用Single模式.
            ServiceBehaviorAttribute sba = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
            if (sba == null)
            {
                sba = new ServiceBehaviorAttribute();
                host.Description.Behaviors.Add(sba);
            }
            sba.ConcurrencyMode = ConcurrencyMode.Multiple;
            sba.InstanceContextMode = InstanceContextMode.Single;
            sba.IncludeExceptionDetailInFaults = true;

            //ServiceMetadataBehavior smb = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
            //if (smb == null)
            //{
            //    smb = new ServiceMetadataBehavior();
            //    host.Description.Behaviors.Add(smb);
            //}

            //TcpTransportBindingElement tbe = new TcpTransportBindingElement();
            //tbe.PortSharingEnabled = true;
            //host.AddServiceEndpoint(typeof(IMetadataExchange), new CustomBinding(tbe), "MEX");

            host.Open();

            hostList.Add(sd, host);
            return true;
        }
    }
}
